package han.cirno.corrupt.components.monet

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb

object MonetGenerator {
    enum class ColorTone{
        DEFAULT,
        SYSTEM_ACCENT_1,
        SYSTEM_ACCENT_2,
        SYSTEM_ACCENT_3,
        SYSTEM_NATURAL_1,
        SYSTEM_NATURAL_2
    }

    private val tones = 0..100//listOf(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100)

    fun generateColors(
        seedColorLight: Int,
        seedColorDark:Int,
        colorTone: ColorTone=ColorTone.DEFAULT): MonetPalette {
        return MonetPaletteImpl(
            generateMonetLightHalfPalette(seedColorLight,colorTone),
            generateMonetDarkHalfPalette(seedColorDark,colorTone),
        )
    }

    private fun generateMonetLightHalfPalette(seedColor: Int,colorTone: ColorTone):HalfMonetPalette{
        val monetPalette = generateMonetPalette(seedColor)
        return if (colorTone==ColorTone.DEFAULT) {
            val accent1 = monetPalette[ColorTone.SYSTEM_ACCENT_1]!!
            val accent2 = monetPalette[ColorTone.SYSTEM_ACCENT_2]!!
            val accent3 = monetPalette[ColorTone.SYSTEM_ACCENT_3]!!
            val neutral1 = monetPalette[ColorTone.SYSTEM_NATURAL_1]!!
            val neutral2 = monetPalette[ColorTone.SYSTEM_NATURAL_2]!!
            HalfMonetPalette(
                primary = accent1[40]!!,
                onPrimary = accent1[100]!!,
                primaryContainer = accent1[90]!!,
                onPrimaryContainer = accent1[10]!!,
                secondary = accent2[40]!!,
                onSecondary = accent2[100]!!,
                secondaryContainer = accent2[90]!!,
                onSecondaryContainer = accent2[10]!!,
                tertiary = accent3[40]!!,
                onTertiary = accent3[100]!!,
                tertiaryContainer = accent3[90]!!,
                onTertiaryContainer = accent3[10]!!,
                error = valueOf("#FFB4AB"),
                onError = valueOf("#690005"),
                errorContainer = valueOf("#FFDAD6"),
                onErrorContainer = valueOf("#410002"),
                background = neutral1[99]!!,
                onBackground = neutral1[10]!!,
                surface = neutral1[99]!!,
                onSurface = neutral1[10]!!,
                surfaceVariant = neutral2[90]!!,
                onSurfaceVariant = neutral2[30]!!,
                outline = neutral2[50]!!,
                outlineVariant = neutral2[80]!!,
                scrim = Color.Black,
                inverseSurface = neutral1[20]!!,
                inverseOnSurface = neutral1[95]!!,
                inversePrimary = accent1[80]!!,
                surfaceDim = neutral1[87]!!,
                surfaceBright = neutral1[98]!!,
                surfaceContainerLowest = Color.White,
                surfaceContainerLow = neutral1[96]!!,
                surfaceContainer = neutral1[94]!!,
                surfaceContainerHigh = neutral1[92]!!,
                surfaceContainerHighest = neutral1[90]!!,
            )
        }else{
            val palette=monetPalette[colorTone]!!
            HalfMonetPalette(
                primary = palette[40]!!,
                onPrimary = palette[100]!!,
                primaryContainer = palette[90]!!,
                onPrimaryContainer = palette[10]!!,
                secondary = palette[40]!!,
                onSecondary = palette[100]!!,
                secondaryContainer = palette[90]!!,
                onSecondaryContainer = palette[10]!!,
                tertiary = palette[40]!!,
                onTertiary = palette[100]!!,
                tertiaryContainer = palette[90]!!,
                onTertiaryContainer = palette[10]!!,
                error = valueOf("#FFB4AB"),
                onError = valueOf("#690005"),
                errorContainer = valueOf("#FFDAD6"),
                onErrorContainer = valueOf("#410002"),
                background = palette[99]!!,
                onBackground = palette[10]!!,
                surface = palette[99]!!,
                onSurface = palette[10]!!,
                surfaceVariant = palette[90]!!,
                onSurfaceVariant = palette[30]!!,
                outline = palette[50]!!,
                outlineVariant = palette[80]!!,
                scrim = Color.Black,
                inverseSurface = palette[20]!!,
                inverseOnSurface = palette[95]!!,
                inversePrimary = palette[80]!!,
                surfaceDim = palette[87]!!,
                surfaceBright = palette[98]!!,
                surfaceContainerLowest = Color.White,
                surfaceContainerLow = palette[96]!!,
                surfaceContainer = palette[94]!!,
                surfaceContainerHigh = palette[92]!!,
                surfaceContainerHighest = palette[90]!!,
            )
        }
    }

    private fun generateMonetDarkHalfPalette(seedColor: Int,colorTone: ColorTone):HalfMonetPalette{
        val monetPalette = generateMonetPalette(seedColor)
        return if (colorTone==ColorTone.DEFAULT) {
            val accent1 = monetPalette[ColorTone.SYSTEM_ACCENT_1]!!
            val accent2 = monetPalette[ColorTone.SYSTEM_ACCENT_2]!!
            val accent3 = monetPalette[ColorTone.SYSTEM_ACCENT_3]!!
            val neutral1 = monetPalette[ColorTone.SYSTEM_NATURAL_1]!!
            val neutral2 = monetPalette[ColorTone.SYSTEM_NATURAL_2]!!
            HalfMonetPalette(
                primary = accent1[80]!!,
                onPrimary = accent1[20]!!,
                primaryContainer = accent1[30]!!,
                onPrimaryContainer = accent1[90]!!,
                secondary = accent2[80]!!,
                onSecondary = accent2[20]!!,
                secondaryContainer = accent2[30]!!,
                onSecondaryContainer = accent2[90]!!,
                tertiary = accent3[80]!!,
                onTertiary = accent3[20]!!,
                tertiaryContainer = accent3[30]!!,
                onTertiaryContainer = accent3[90]!!,
                error = valueOf("#FFB4AB"),
                onError = valueOf("#93000A"),
                errorContainer = valueOf("#B00020"),
                onErrorContainer = valueOf("#FFDAD6"),
                background = neutral1[10]!!,
                onBackground = neutral1[90]!!,
                surface = neutral1[10]!!,
                onSurface = neutral1[80]!!,
                surfaceVariant = neutral2[30]!!,
                onSurfaceVariant = neutral2[80]!!,
                outline = neutral2[60]!!,
                outlineVariant = neutral2[30]!!,
                scrim = Color.Black,
                inverseSurface = neutral1[90]!!,
                inverseOnSurface = neutral1[20]!!,
                inversePrimary = accent1[40]!!,
                surfaceDim = neutral1[6]!!,
                surfaceBright = neutral1[24]!!,
                surfaceContainerLowest = neutral1[4]!!,
                surfaceContainerLow = neutral1[10]!!,
                surfaceContainer = neutral1[12]!!,
                surfaceContainerHigh = neutral1[17]!!,
                surfaceContainerHighest = neutral1[22]!!
            )
        }else{
            val palette = monetPalette[colorTone]!!
            HalfMonetPalette(
                primary = palette[80]!!,
                onPrimary = palette[20]!!,
                primaryContainer = palette[30]!!,
                onPrimaryContainer = palette[90]!!,
                secondary = palette[80]!!,
                onSecondary = palette[20]!!,
                secondaryContainer = palette[30]!!,
                onSecondaryContainer = palette[90]!!,
                tertiary = palette[80]!!,
                onTertiary = palette[20]!!,
                tertiaryContainer = palette[30]!!,
                onTertiaryContainer = palette[90]!!,
                error = valueOf("#FFB4AB"),
                onError = valueOf("#93000A"),
                errorContainer = valueOf("#B00020"),
                onErrorContainer = valueOf("#FFDAD6"),
                background = palette[10]!!,
                onBackground = palette[90]!!,
                surface = palette[10]!!,
                onSurface = palette[80]!!,
                surfaceVariant = palette[30]!!,
                onSurfaceVariant = palette[80]!!,
                outline = palette[60]!!,
                outlineVariant = palette[30]!!,
                scrim = Color.Black,
                inverseSurface = palette[90]!!,
                inverseOnSurface = palette[20]!!,
                inversePrimary = palette[40]!!,
                surfaceDim = palette[6]!!,
                surfaceBright = palette[24]!!,
                surfaceContainerLowest = palette[4]!!,
                surfaceContainerLow = palette[10]!!,
                surfaceContainer = palette[12]!!,
                surfaceContainerHigh = palette[17]!!,
                surfaceContainerHighest = palette[22]!!
            )
        }
    }

    private fun generateMonetPalette(seedColor: Int): Map<ColorTone, Map<Int, Color>> {
        val hsl = rgbToHsl(seedColor)
        val hue = hsl[0]
        val sat = hsl[1]
        val lightness = hsl[2]
        val chromaFactor = calculateChromaFactor(sat, lightness)
        return mapOf(
            ColorTone.SYSTEM_ACCENT_1 to generateAccentPalette(hue, chromaFactor, 0f),
            ColorTone.SYSTEM_ACCENT_2 to generateAccentPalette(hue, chromaFactor * 0.75f, 30f),
            ColorTone.SYSTEM_ACCENT_3 to generateAccentPalette(hue, chromaFactor * 0.5f, 60f),
            ColorTone.SYSTEM_NATURAL_1 to generateNeutralPalette(hue, chromaFactor * 0.15f),
            ColorTone.SYSTEM_NATURAL_2 to generateNeutralPalette(hue, chromaFactor * 0.08f)
        )
    }

    private fun generateAccentPalette(
        baseHue: Float,
        chromaFactor: Float,
        hueShift: Float
    ): Map<Int, Color> {
        return tones.associateWith { tone ->
            val adjustedHue = (baseHue + hueShift) % 360
            val adjustedChroma = when (tone) {
                in 0..40 -> chromaFactor * (0.3f + tone / 100f)
                in 50..60 -> chromaFactor * 0.8f
                in 70..80 -> chromaFactor * 0.6f
                else -> chromaFactor * (1.2f - tone / 100f)
            }
            val lightness = tone / 100f
            Color(hslToRgb(adjustedHue, adjustedChroma.coerceIn(0f, 0.4f), lightness))
        }
    }

    private fun generateNeutralPalette(
        baseHue: Float,
        chromaFactor: Float
    ): Map<Int, Color> {
        return tones.associateWith { tone ->
            val lightness = tone / 100f
            val chroma = when (tone) {
                in 0..30 -> chromaFactor * 0.3f
                in 40..60 -> chromaFactor * 0.2f
                else -> chromaFactor * 0.1f
            }
            Color(hslToRgb(baseHue, chroma.coerceIn(0f, 0.1f), lightness))
        }
    }

    private fun calculateChromaFactor(saturation: Float, lightness: Float): Float {
        return when {
            lightness < 0.2f -> 0.6f * saturation
            lightness > 0.8f -> 0.8f * saturation
            else -> saturation * (0.8f + 0.2f * (0.5f - abs(lightness - 0.5f)))
        }
    }

    private fun rgbToHsl(rgbColor: Int): FloatArray {
        val color = Color(rgbColor)
        val r = color.red
        val g = color.green
        val b = color.blue
        val max = maxOf(r, g, b)
        val min = minOf(r, g, b)
        var h: Float
        val s: Float
        val l = (max + min) / 2f
        if (max == min) {
            h = 0f
            s = 0f
        } else {
            val d = max - min
            s = if (l > 0.5f) d / (2f - max - min) else d / (max + min)
            h = when (max) {
                r -> (g - b) / d + (if (g < b) 6f else 0f)
                g -> (b - r) / d + 2f
                else -> (r - g) / d + 4f
            }
            h *= 60f
            if (h < 0) h += 360f
        }

        return floatArrayOf(h, s, l)
    }

    private fun hslToRgb(h: Float, s: Float, l: Float): Int {
        val q = if (l < 0.5f) l * (1f + s) else l + s - (l * s)
        val p = 2f * l - q
        val hk = h / 360f
        val tr = (hk + 1f / 3f).coerceIn(0f, 1f)
        val tg = hk.coerceIn(0f, 1f)
        val tb = (hk - 1f / 3f).coerceIn(0f, 1f)
        val r = hueToRgb(p, q, tr)
        val g = hueToRgb(p, q, tg)
        val b = hueToRgb(p, q, tb)
        return Color(r,g,b).toArgb()
    }

    private fun hueToRgb(p: Float, q: Float, t: Float): Float {
        val tt = when {
            t < 0 -> t + 1f
            t > 1 -> t - 1f
            else -> t
        }
        return when {
            tt < 1f / 6f -> p + (q - p) * 6f * tt
            tt < 1f / 2f -> q
            tt < 2f / 3f -> p + (q - p) * (2f / 3f - tt) * 6f
            else -> p
        }
    }
    private fun abs(x: Float) = if (x < 0) -x else x
    private fun valueOf(colorHex:String):Color=
        Color(android.graphics.Color.parseColor(colorHex))
}
